package com.mobilekites.api.model;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONObject;

import com.android.volley.Request;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;

public abstract class PaginatedList<T> extends DfeModel implements
		Listener<JSONObject> {

	protected boolean mAutoLoadNextPage;
	protected Request<JSONObject> mCurrentRequest;
	private List<T> mItems;
	protected JSONObject mLastResponse;
	private int mItemsUntilEndCount;
	private boolean mMoreAvailable;
	private int mCurrentPage;
	private int mPrePage;

	protected PaginatedList(int page, boolean autoLoadNextPage) {
		mItems = new ArrayList<T>();
		mItemsUntilEndCount = 4;
		mMoreAvailable = true;
		mAutoLoadNextPage = autoLoadNextPage;
		mCurrentPage = page;
		mPrePage = mCurrentPage - 1;
	}

	protected PaginatedList(int page) {
		this(page, true);
	}

	private void updateItemsUntilEndCount(int i) {
		if (mItemsUntilEndCount > 0)
			mItemsUntilEndCount = Math.max(1, i / 4);
		else
			mItemsUntilEndCount = 4;
	}

	public void clearDataAndReplaceInitialParams() {
		mCurrentPage = 0;
		mPrePage = mCurrentPage - 1;
		resetItems();
	}

	public void clearTransientState() {
		mCurrentRequest = null;
	}

	public int getCount() {
		return mItems.size();
	}

	/**
	 * Parse items from response from Server
	 * 
	 * @param response
	 *            response from Server, whether: json, xml, string...
	 * @return items list data, always return list !=null, if parse error must
	 *         return list has no item
	 */
	protected abstract List<T> getItemsFromResponse(JSONObject response);

	protected abstract boolean checkMoreAvaiable(List<T> response);

	public boolean isMoreAvailable() {
		return mMoreAvailable;
	}

	public boolean isReady() {
		return mLastResponse == null && mItems.size() <= 0 ? false : true;
	}

	protected abstract Request<JSONObject> makeRequest(int page);

	public void onErrorResponse(VolleyError volleyerror) {
		clearTransientState();
		super.onErrorResponse(volleyerror);
	}

	public void resetItems() {
		mMoreAvailable = true;
		mItems.clear();
		notifyDataSetChanged();
	}

	public void retryLoadItems() {
		if (!inErrorState()) {
			return;
		}
		clearTransientState();
		clearErrors();
		mPrePage = mCurrentPage - 1;
		requestMoreItemsIfNoRequestExists(mCurrentPage);
	}

	public void startLoadItems() {
		if (mMoreAvailable && getCount() == 0) {
			clearErrors();
			mPrePage = mCurrentPage - 1;
			requestMoreItemsIfNoRequestExists(mCurrentPage);
		}
	}

	private void requestMoreItemsIfNoRequestExists(int page) {
		if (inErrorState()) {
			return;
		}
		if (isDuplicateRequest(page))
			return;
		if (mCurrentRequest != null)
			mCurrentRequest.cancel();
		mCurrentRequest = makeRequest(page);
		mPrePage = page;
	}

	private boolean isDuplicateRequest(int page) {
		return mPrePage == page ? true : false;
	}

	public T getItem(int position) {
		return getItem(position, true);
	}

	public final T getItem(int position, boolean isLoad) {
		if (position < 0)
			throw new IllegalArgumentException((new StringBuilder())
					.append("Can't return an item with a negative index: ")
					.append(position).toString());

		if (position >= getCount())
			return null;
		T obj = mItems.get(position);
		if (mAutoLoadNextPage && mMoreAvailable
				&& position >= getCount() - mItemsUntilEndCount) {
			if (isLoad)
				requestMoreItemsIfNoRequestExists(mCurrentPage);
		}
		return obj;
	}

	protected abstract int getNextPage(List<T> list, int currentPage);

	@Override
	public void onResponse(JSONObject response) {
		clearErrors();
		mLastResponse = response;
		List<T> list = getItemsFromResponse(response);
		updateItemsUntilEndCount(list.size());
		mItems.addAll(list);
		// increase param to start next request
		// mCurrentParam = new Param(mNextParam.getParams());
		// mNextParam.setParams(mNextParam.getNextParams());
		mCurrentPage = getNextPage(list, mCurrentPage);

		// mNextParam.setParams(mNextParam.getNextParams());
		if (!mAutoLoadNextPage) {
			clearTransientState();
			mMoreAvailable = false;
		} else
			mMoreAvailable = checkMoreAvaiable(list);

		notifyDataSetChanged();
	}
}
